// 云对象教程: https://uniapp.dcloud.net.cn/uniCloud/cloud-obj
// jsdoc语法提示教程：https://ask.dcloud.net.cn/docs/#//ask.dcloud.net.cn/article/129
const createConfig = require('uni-config-center');
const buildTemplateData = require('./build-template-data');
const { parserDynamicField, checkIsStaticTemplate } = require('./utils');
const schemaNameAdapter = require('./schema-name-adapter');

const uniSmsCo = uniCloud.importObject('uni-sms-co');
const db = uniCloud.database();
const smsConfig = createConfig({
    pluginId: 'uni-sms-co',
}).config();

function errCode(code) {
    return 'uni-sms-co-' + code;
}

const tableNames = {
    template: 'opendb-sms-template',
    task: 'opendb-sms-task',
    log: 'opendb-sms-log',
};

module.exports = {
    _before: async function () {
        // 通用预处理器
        if (
            !smsConfig.smsKey ||
            smsConfig.smsKey.length <= 20 ||
            !smsConfig.smsSecret ||
            smsConfig.smsSecret.length <= 20
        ) {
            throw new Error('请先配置smsKey和smsSecret');
        }

        this.tableNames = tableNames;

        /**
         * 优化 schema 的命名规范，需要兼容 uni-admin@2.1.6 以下版本
         * 如果是在uni-admin@2.1.6版本以上创建的项目可以将其注释
         * */
        await schemaNameAdapter.call(this);
    },
    _after: function (error, result) {
        if (error) {
            if (error instanceof Error) {
                return {
                    errCode: 'error',
                    errMsg: error.message,
                };
            }

            if (error.errCode) {
                return error;
            }

            throw error;
        }

        return result;
    },
    /**
     * 创建短信任务
     * @param {Object} to
     * @param {Boolean} to.all=false 全部用户发送
     * @param {String} to.type=user to.all=true时用来区分发送类型
     * @param {Array} to.receiver 用户ID's / 用户标签ID's
     * @param {String} templateId 短信模板ID
     * @param {Array} templateData 短信模板数据
     * @param {String} options.taskName 任务名称
     */
    async createSmsTask(to, templateId, templateData, options = {}) {
        if (!templateId) {
            return {
                errCode: errCode('template-id-required'),
                errMsg: '缺少templateId',
            };
        }

        if (!to.all && (!to.receiver || to.receiver.length <= 0)) {
            return {
                errCode: errCode('send-users-is-null'),
                errMsg: '请选择要发送的用户',
            };
        }

        const clientInfo = this.getClientInfo();

        const { data: templates } = await db.collection(this.tableNames.template).where({ _id: templateId }).get();
        if (templates.length <= 0) {
            return {
                errCode: errCode('template-not-found'),
                errMsg: '短信模板不存在',
            };
        }
        const [template] = templates;

        // 创建短信任务
        const task = await db.collection(this.tableNames.task).add({
            app_id: clientInfo.appId,
            name: options.taskName,
            template_id: templateId,
            template_contnet: template.content,
            vars: templateData,
            to,
            send_qty: 0,
            success_qty: 0,
            fail_qty: 0,
            create_date: Date.now(),
        });

        uniSmsCo.createUserSmsMessage(task.id);

        return new Promise((resolve) =>
            setTimeout(
                () =>
                    resolve({
                        errCode: 0,
                        errMsg: '任务创建成功',
                        taskId: task.id,
                    }),
                300
            )
        );
    },
    async createUserSmsMessage(taskId, execData = {}) {
        const parallel = 100;
        let beforeId;
        const { data: tasks } = await db.collection(this.tableNames.task).where({ _id: taskId }).get();

        if (tasks.length <= 0) {
            return {
                errCode: errCode('task-id-not-found'),
                errMsg: '任务ID不存在',
            };
        }

        const [task] = tasks;
        const query = {
            mobile: db.command.exists(true),
        };

        // 指定用户发送
        if (!task.to.all && task.to.type === 'user') {
            let index = 0;
            if (execData.beforeId) {
                const i = task.to.receiver.findIndex((id) => id === execData.beforeId);
                index = i !== -1 ? i + 1 : 0;
            }

            const receiver = task.to.receiver.slice(index, index + parallel);
            query._id = db.command.in(receiver);
            beforeId = receiver[receiver.length - 1];
        }

        // 指定用户标签
        if (task.to.type === 'userTags') {
            query.tags = db.command.in(task.to.receiver);
        }

        // 全部用户
        if (task.to.all && execData.beforeId) {
            query._id = db.command.gt(execData.beforeId);
        }

        // 动态数据仅支持uni-id-users表字段
        const dynamicField = parserDynamicField(task.vars);
        const userFields = dynamicField['uni-id-users']
            ? dynamicField['uni-id-users'].reduce((res, field) => {
                  res[field] = true;
                  return res;
              }, {})
            : {};
        const { data: users } = await db
            .collection('uni-id-users')
            .where(query)
            .field({
                mobile: true,
                ...userFields,
            })
            .limit(parallel)
            .orderBy('_id', 'asc')
            .get();

        if (users.length <= 0) {
            // 更新要发送的短信数量
            const count = await db.collection(this.tableNames.log).where({ task_id: taskId }).count();
            await db.collection(this.tableNames.task).where({ _id: taskId }).update({
                send_qty: count.total,
            });

            // 开始发送
            uniSmsCo.sendSms(taskId);

            return new Promise((resolve) =>
                setTimeout(
                    () =>
                        resolve({
                            errCode: 0,
                            errMsg: '创建完成',
                        }),
                    500
                )
            );
        }

        if (!beforeId) {
            beforeId = users[users.length - 1]._id;
        }

        let docs = [];
        for (const user of users) {
            const varData = await buildTemplateData(task.vars, user);
            docs.push({
                uid: user._id,
                task_id: taskId,
                mobile: user.mobile,
                var_data: varData,
                status: 0,
                create_date: Date.now(),
            });
        }

        await db.collection(this.tableNames.log).add(docs);

        uniSmsCo.createUserSmsMessage(taskId, { beforeId });

        return new Promise((resolve) => setTimeout(() => resolve(), 500));
    },
    async sendSms(taskId) {
        const { data: tasks } = await db.collection(this.tableNames.task).where({ _id: taskId }).get();
        if (tasks.length <= 0) {
            console.warn(`task [${taskId}] not found`);
            return;
        }

        const [task] = tasks;
        const isStaticTemplate = !task.vars.length;

        let sendData = {
            appId: task.app_id,
            smsKey: smsConfig.smsKey,
            smsSecret: smsConfig.smsSecret,
            templateId: task.template_id,
            data: {},
        };

        const { data: records } = await db
            .collection(this.tableNames.log)
            .where({ task_id: taskId, status: 0 })
            .limit(isStaticTemplate ? 50 : 1)
            .field({ mobile: true, var_data: true })
            .get();

        if (records.length <= 0) {
            return {
                errCode: 0,
                errMsg: '发送完成',
            };
        }

        if (isStaticTemplate) {
            sendData.phoneList = records.reduce((res, user) => {
                res.push(user.mobile);
                return res;
            }, []);
        } else {
            const [record] = records;
            sendData.phone = record.mobile;
            sendData.data = record.var_data;
        }

        try {
            //   await sendSms(sendData)
            await uniCloud.sendSms(sendData);
            // 修改发送状态为已发送
            await db
                .collection(this.tableNames.log)
                .where({
                    _id: db.command.in(records.map((record) => record._id)),
                })
                .update({
                    status: 1,
                    send_date: Date.now(),
                });
            // 更新任务的短信成功数
            await db
                .collection(this.tableNames.task)
                .where({ _id: taskId })
                .update({
                    success_qty: db.command.inc(records.length),
                });
        } catch (e) {
            console.error('[sendSms Fail]', e);
            // 修改发送状态为发送失败
            await db
                .collection(this.tableNames.log)
                .where({
                    _id: db.command.in(records.map((record) => record._id)),
                })
                .update({
                    status: 2,
                    reason: e.errMsg || '未知原因',
                    send_date: Date.now(),
                });
            // 更新任务的短信失败数
            await db
                .collection(this.tableNames.task)
                .where({ _id: taskId })
                .update({
                    fail_qty: db.command.inc(records.length),
                });
        }

        uniSmsCo.sendSms(taskId);

        return new Promise((resolve) => setTimeout(() => resolve(), 500));
    },
    async template() {
        const { data: templates = [] } = await db.collection(this.tableNames.template).get();
        return templates;
    },
    async task(id) {
        const { data: tasks } = await db.collection(this.tableNames.task).where({ _id: id }).get();
        if (tasks.length <= 0) {
            return null;
        }

        return tasks[0];
    },
    async updateTemplates(templates) {
        if (templates.length <= 0) {
            return {
                errCode: errCode('template-is-null'),
                errMsg: '缺少模板信息',
            };
        }

        let group = [];
        for (const template of templates) {
            group.push(
                db.collection(this.tableNames.template).doc(String(template.templateId)).set({
                    name: template.templateName,
                    content: template.templateContent,
                    type: template.templateType,
                    sign: template.templateSign,
                })
            );
        }

        await Promise.all(group);

        return {
            errCode: 0,
            errMsg: '更新成功',
        };
    },
    async preview(to, templateId, templateData) {
        const count = 1;
        let query = {
            mobile: db.command.exists(true),
        };

        // 指定用户发送
        if (!to.all && to.type === 'user') {
            const receiver = to.receiver.slice(0, 10);
            query._id = db.command.in(receiver);
        }

        // 指定用户标签
        if (to.type === 'userTags') {
            query.tags = db.command.in(to.receiver);
        }

        const { data: users } = await db.collection('uni-id-users').where(query).limit(count).get();
        console.log({ users, query });
        if (users.length <= 0) {
            return {
                errCode: errCode('users-is-null'),
                errMsg: '请添加要发送的用户',
            };
        }

        const { data: templates } = await db.collection(this.tableNames.template).where({ _id: templateId }).get();
        if (templates.length <= 0) {
            return {
                errCode: errCode('template-not-found'),
                errMsg: '模板不存在',
            };
        }
        const [template] = templates;

        let docs = [];
        for (const user of users) {
            const varData = buildTemplateData(templateData, user);
            const content = template.content.replace(/\$\{(.*?)\}/g, ($1, param) => varData[param] || $1);
            docs.push(`【${template.sign}】${content}`);
        }

        return {
            errCode: 0,
            errMsg: '',
            list: docs,
        };
    },
};
